Skip to content

Add support for remaining config variables from the old to-be-deprecated Config interface and add a new config class - #758

Open
ubaskota wants to merge 3 commits into
smithy-lang:developfrom
ubaskota:config_var_support_implementation
Open

Add support for remaining config variables from the old to-be-deprecated Config interface and add a new config class#758
ubaskota wants to merge 3 commits into
smithy-lang:developfrom
ubaskota:config_var_support_implementation

Conversation

@ubaskota

@ubaskota ubaskota commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Issue #, if available:

Description of changes:
Adds the remaining AWS-shared config fields to AsyncAwsConfig, bringing it to parity with the generated service Config class.

  • New fields: Adds support for endpoint_uri, aws_access_key_id, aws_secret_access_key, aws_session_token, sdk_ua_app_id, user_agent_extra, interceptors, http_request_config, transport, retry_strategy, aws_credentials_identity_resolver. Resolvable fields wire into the env > profile > default resolution pipeline.
  • Service-specific codegen: Generates Async<ServiceId>Config(AsyncAwsConfig) with service-specific _FIELDS that override the base class example: endpoint_uri uses a service-aware resolver that checks AWS_ENDPOINT_URL_<SERVICE_ID> and the services config section before falling back to global sources.
  • Dual config support: The generated config module now contains both the old Config (with a deprecation warning) and the new Async<ServiceId>Config, so existing users continue to work while new users adopt the async resolution path. The generated client accepts either type via isinstance dispatch.
  • Supporting changes: Adds get_service_config() on MergedConfig for services-section lookups, and updates RetryStrategyResolver to accept retry_mode/max_attempts fallbacks from the config layer.

Testing:

  • Added unit tests for:
    • EndpointUriResolver covering the full precedence chain: service-specific env var > global env var > service config section > global profile > unset.
    • MergedConfig.get_service_config() covering all lookup paths (profile missing, services key missing, service section not found, multiple services).
    • RetryStrategyResolver fallback behavior: retry_mode/max_attempts params used when retry_strategy is None, explicit strategy takes precedence over fallbacks.

Example Usage:

Resolve service config and inspect provenance:

# With AWS_REGION=us-east-1 set in the environment
# and ~/.aws/config containing:
#   [profile default]
#   services = my-services
#
#   [services my-services]
#   bedrock_runtime =
#     endpoint_url = https://bedrock-runtime.us-east-1.amazonaws.com
import asyncio
from aws_sdk_bedrock_runtime.config import AsyncBedrockRuntimeConfig

async def main():
  config = await AsyncBedrockRuntimeConfig.resolve()

  print(config.region)                    # "us-east-1"
  print(config.source_of("region"))       # ENV
  print(config.endpoint_uri)              # "https://bedrock-runtime.us-east-1.amazonaws.com"
  print(config.source_of("endpoint_uri")) # PROFILE

asyncio.run(main())

Invalid profile raises a clear error:

import asyncio
from aws_sdk_bedrock_runtime.config import AsyncBedrockRuntimeConfig

async def main():
  config = await AsyncBedrockRuntimeConfig.resolve(profile="non-existent")
  # raises ProfileNotFoundError:
  #   Profile 'non-existent' (from the profile argument) not found in config file.

asyncio.run(main())

Refer to #751 for more examples.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@ubaskota
ubaskota requested a review from a team as a code owner July 30, 2026 04:25
@ubaskota ubaskota changed the title Add support for remaining config variables from the old to-be-deprecated Config interface Add support for remaining config variables from the old to-be-deprecated Config interface and add a new config class Jul 30, 2026

@arandito arandito left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ubaskota! I left a couple comments but my biggest concern is how we are resolving environment and profile credentials during config resolution. Config resolution should only handle in-code credentials and defer env/profile credentials to the new IdentityChain. Let me know if you have any questions!

Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/resolvers.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py

@jonathan343 jonathan343 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Ujjwal. I let some comments on the areas I'm most concerned about. Let me know if you have any questions.

Also, you need to rebase this PR with the latest from develop.

I'm still investigating some additional cleanup that probably should be done, but wanted to get you some feedback so you have something to work on in the meantime.

$3C
self._config = config or $1T()

client_plugins: list[$2T] = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you decide to move client_plugins this out of the client constructor? It's now generated inside of every operation which means we are re-allocating every time. Unless there is a good reason, I think this should stay in the class constructor as it exists today.

Comment on lines +124 to +125
writer.writeDocs("The protocol to serialize and deserialize requests with.", context);
writer.write("");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple config options that get generated with trailing whitespace in their docstrings:

"""The protocol to serialize and deserialize requests with.    """

This should be:

"""The protocol to serialize and deserialize requests with.    """

Can you investigate this bug and compare with the existing Config object to see why there is this difference?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package should have a brief but descriptive changelog entry for the changes being made in this PR. Our packages get version bumped based on pending entries. Right now you're relying on existing entries to get version bumped which we shouldn't do.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also add an entry here to ensure this is released with the other changes.

""";

// Variant for services without a generated async config, which must not be referenced.
private static final String USER_AGENT_PLUGIN_SYNC_ONLY = """

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which services won't have an async config? Shouldn't they all have the async config right now?

Comment on lines +286 to +291
if (asyncConfigForPlugin.isPresent()) {
writer.write("$L: TypeAlias = Callable[[$T | $T], None]",
plugin.getName(), config, asyncConfigForPlugin.get());
} else {
writer.write("$L: TypeAlias = Callable[[$T], None]", plugin.getName(), config);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugin API introduced in this PR doesn't make sense to me. Currently I see the following get generated:

AsyncBedrockRuntimePlugin: TypeAlias = Callable[[AsyncBedrockRuntimeConfig], None]
"""
A callable that allows customizing the async config object on each
request.
"""

Plugin: TypeAlias = Callable[[Config | AsyncBedrockRuntimeConfig], None]
"""A callable that allows customizing the config object on each request."""
  1. I don't see AsyncBedrockRuntimePlugin actually being used or referenced anywhere.
  2. The Async naming prefix seems misleading since there is no async work done by the plugins.
  3. The Callable[[Config | AsyncBedrockRuntimeConfig], None] signature is not what we want. This will make all plugins need to accept both config options. See below for what I think it should be.

IMO, during migration, we should generate the following:

Plugin: TypeAlias = (
    Callable[[Config], None]
    | Callable[[AsyncBedrockRuntimeConfig], None]
)

After we remove support for Config it should just become:

Plugin: TypeAlias = Callable[[AsyncBedrockRuntimeConfig], None]

}

// Write _FIELDS class variable with service-specific defaults
writer.openBlock("_FIELDS: ClassVar[dict[str, $T]] = {", fieldSpecSymbol);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This emits something like below:

    _FIELDS: ClassVar[dict[str, FieldSpec]] = {
        "aws_credentials_identity_resolver": FieldSpec(default=None),
        "region": FieldSpec(default=None),
        "aws_access_key_id": FieldSpec(default=None),
        "aws_secret_access_key": FieldSpec(default=None),
        "aws_session_token": FieldSpec(default=None),
        "user_agent_extra": FieldSpec(default=None),
        "sdk_ua_app_id": FieldSpec(default=None),
        **AsyncAwsConfig._FIELDS,
        "endpoint_uri": FieldSpec(
            default=None, resolver=EndpointUriResolver("bedrock_runtime")
        ),
        "endpoint_resolver": FieldSpec(
            default_factory=lambda: StandardRegionalEndpointsResolver(
                endpoint_prefix="bedrock-runtime"
            )
        ),
        "protocol": FieldSpec(
            default_factory=lambda: RestJsonClientProtocol(
                _SCHEMA_AMAZON_BEDROCK_FRONTEND_SERVICE
            )
        ),
        "auth_schemes": FieldSpec(
            default_factory=lambda: {
                ShapeID("aws.auth#sigv4"): SigV4AuthScheme(service="bedrock")
            }
        ),
        "auth_scheme_resolver": FieldSpec(default_factory=HTTPAuthSchemeResolver),
        "transport": FieldSpec(default_factory=lambda: AWSCRTHTTPClient()),
    }

It's not clean to my why we're emitting inherited fields here that I though would be covered by **AsyncAwsConfig._FIELDS,.

I was expecting to see something closer to:

 _FIELDS = {
      **AsyncAwsConfig._FIELDS,
      "endpoint_uri": ...,
      "endpoint_resolver": ...,
      "protocol": ...,
      "auth_schemes": ...,
      "auth_scheme_resolver": ...,
      "transport": ...,
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants